/*
 * Copyright 2016 Red Hat, Inc. and/or its affiliates
 * and other contributors as indicated by the @author tags.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.keycloak.examples.authenticator;

import static org.keycloak.common.util.ServerCookie.SameSiteAttributeValue.NONE;

import java.net.URI;

import javax.ws.rs.core.Cookie;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;

import org.jboss.resteasy.spi.HttpResponse;
import org.jboss.resteasy.spi.ResteasyProviderFactory;
import org.keycloak.authentication.AuthenticationFlowContext;
import org.keycloak.authentication.AuthenticationFlowError;
import org.keycloak.authentication.Authenticator;
import org.keycloak.common.util.ServerCookie;
import org.keycloak.models.AuthenticatorConfigModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserCredentialModel;
import org.keycloak.models.UserModel;

/**
 * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
 * @version $Revision: 1 $
 */
public class SecretQuestionAuthenticator implements Authenticator {

	public static final String CREDENTIAL_TYPE = "secret_question";

	protected boolean hasCookie(AuthenticationFlowContext context) {
		Cookie cookie = context.getHttpRequest().getHttpHeaders().getCookies().get("SECRET_QUESTION_ANSWERED");
		boolean result = cookie != null;
		if (result) {
			System.out.println("Bypassing secret question because cookie is set");
		}
		return result;
	}

	@Override
	public void authenticate(AuthenticationFlowContext context) {
		if (hasCookie(context)) {
			context.success();
			return;
		}
		Response challenge = context.form().createForm("secret-question.ftl");
		context.challenge(challenge);
	}

	@Override
	public void action(AuthenticationFlowContext context) {
		MultivaluedMap<String, String> formData = context.getHttpRequest().getDecodedFormParameters();
		if (formData.containsKey("cancel")) {
			context.cancelLogin();
			return;
		}
		boolean validated = validateAnswer(context);
		if (!validated) {
			Response challenge = context.form().setError("badSecret").createForm("secret-question.ftl");
			context.failureChallenge(AuthenticationFlowError.INVALID_CREDENTIALS, challenge);
			return;
		}
		setCookie(context);
		context.success();
	}

	protected void setCookie(AuthenticationFlowContext context) {
		AuthenticatorConfigModel config = context.getAuthenticatorConfig();
		int maxCookieAge = 60 * 60 * 24 * 30; // 30 days
		if (config != null) {
			maxCookieAge = Integer.valueOf(config.getConfig().get("cookie.max.age"));

		}
		URI uri = context.getUriInfo().getBaseUriBuilder().path("realms").path(context.getRealm().getName()).build();
		addCookie("SECRET_QUESTION_ANSWERED", "true", uri.getRawPath(), null, null, maxCookieAge, false, true);
	}

	public static void addCookie(String name, String value, String path, String domain, String comment, int maxAge,
			boolean secure, boolean httpOnly) {
		HttpResponse response = ResteasyProviderFactory.peekInstance().getContextData(HttpResponse.class);
		StringBuffer cookieBuf = new StringBuffer();
		ServerCookie.appendCookieValue(cookieBuf, 1, name, value, path, domain, comment, maxAge, secure, httpOnly,
				NONE);
		String cookie = cookieBuf.toString();
		response.getOutputHeaders().add(HttpHeaders.SET_COOKIE, cookie);
	}

	protected boolean validateAnswer(AuthenticationFlowContext context) {
		MultivaluedMap<String, String> formData = context.getHttpRequest().getDecodedFormParameters();
		String secret = formData.getFirst("secret_answer");
		UserCredentialModel input = new UserCredentialModel("", SecretQuestionCredentialProvider.SECRET_QUESTION,
				secret);
		return context.getUser().credentialManager().isValid(input);
	}

	@Override
	public boolean requiresUser() {
		return true;
	}

	@Override
	public boolean configuredFor(KeycloakSession session, RealmModel realm, UserModel user) {
		return user.credentialManager().isConfiguredFor(SecretQuestionCredentialProvider.SECRET_QUESTION);
	}

	@Override
	public void setRequiredActions(KeycloakSession session, RealmModel realm, UserModel user) {
		user.addRequiredAction(SecretQuestionRequiredAction.PROVIDER_ID);
	}

	@Override
	public void close() {

	}
}
